Next: Internals, Previous: Example Definitions, Up: Lisp Definitions [Contents][Index]
A later section (see Internals) gives a full
description of Calc’s internal Lisp functions. It’s
not hard to call Calc from inside your programs, but the number
of these functions can be daunting. So Calc provides one special
“programmer-friendly” function called
calc-eval that can be made to do just about
everything you need. It’s not as fast as the low-level Calc
functions, but it’s much simpler to use!
It may seem that calc-eval itself has a daunting
number of options, but they all stem from one simple
operation.
In its simplest manifestation, ‘(calc-eval
"1+2")’ parses the string "1+2" as if
it were a Calc algebraic entry and returns the result formatted
as a string: "3".
Since calc-eval is on the list of recommended
autoload functions, you don’t need to make any
special preparations to load Calc before calling
calc-eval the first time. Calc will be loaded and
initialized for you.
All the Calc modes that are currently in effect will be used when evaluating the expression and formatting the result.
calc-evalIf the input string parses to a list of expressions, Calc
returns the results separated by ", ". You can
specify a different separator by giving a second string argument
to calc-eval: ‘(calc-eval "1+2,3+4"
";")’ returns "3;7".
The “separator” can also be any of several Lisp
symbols which request other behaviors from
calc-eval. These are discussed one by one below.
You can give additional arguments to be substituted for
‘$’, ‘$$’, and
so on in the main expression. For example,
‘(calc-eval "$/$$" nil "7" "1+1")’
evaluates the expression "7/(1+1)" to yield the
result "3.5" (assuming Fraction mode is not in
effect). Note the nil used as a placeholder for the
item-separator argument.
If calc-eval encounters an error, it returns a
list containing the character position of the error, plus a
suitable message as a string. Note that ‘1 /
0’ is not an error by Calc’s
standards; it simply returns the string "1 / 0"
which is the division left in symbolic form. But
‘(calc-eval "1/")’ will return the list
‘(2 "Expected a number")’.
If you bind the variable calc-eval-error to
t using a let form surrounding the call
to calc-eval, errors instead call the Emacs
error function which aborts to the Emacs command
loop with a beep and an error message.
If you bind this variable to the symbol string,
error messages are returned as strings instead of lists. The
character position is ignored.
As a courtesy to other Lisp code which may be using Calc, be
sure to bind calc-eval-error using let
rather than changing it permanently with
setq.
Sometimes it is preferable to treat ‘1 /
0’ as an error rather than returning a symbolic
result. If you pass the symbol num as the second
argument to calc-eval, results that are not
constants are treated as errors. The error message reported is
the first calc-why message if there is one, or
otherwise “Number expected.”
A result is “constant” if it is a number, vector, or other object that does not include variables or function calls. If it is a vector, the components must themselves be constants.
If the first argument to calc-eval is a list
whose first element is a formula string, then
calc-eval sets all the various Calc modes to their
default values while the formula is evaluated and formatted. For
example, the precision is set to 12 digits, digit grouping is
turned off, and the Normal language mode is used.
This same principle applies to the other options discussed below. If the first argument would normally be x, then it can also be the list ‘(x)’ to use the default mode settings.
If there are other elements in the list, they are taken as
variable-name/value pairs which override the default mode
settings. Look at the documentation at the front of the
calc.el file to find the names of the Lisp variables
for the various modes. The mode settings are restored to their
original values when calc-eval is done.
For example, ‘(calc-eval '("$+$$" calc-internal-prec 8) 'num a b)’ computes the sum of two numbers, requiring a numeric result, and using default mode settings except that the precision is 8 instead of the default of 12.
It’s usually best to use this form of
calc-eval unless your program actually considers the
interaction with Calc’s mode settings to be a feature. This
will avoid all sorts of potential “gotchas”; consider
what happens with ‘(calc-eval "sqrt(2)"
'num)’ when the user has left Calc in Symbolic mode
or No-Simplify mode.
As another example, ‘(equal (calc-eval
'("$<$$") nil a b) "1")’ checks if the number in
string ‘a’ is less than the one in
string ‘b’. Without using a list, the
integer 1 might come out in a variety of formats which would be
hard to test for conveniently: "1",
"8#1", "00001". (But see
“Predicates” mode, below.)
Normally all input and output for calc-eval is
done with strings. You can do arithmetic with, say,
‘(calc-eval "$+$$" nil a b)’ in place of
‘(+ a b)’, but this is very inefficient
since the numbers must be converted to and from string format as
they are passed from one calc-eval to the next.
If the separator is the symbol raw, the result
will be returned as a raw Calc data structure rather than a
string. You can read about how these objects look in the
following sections, but usually you can treat them as
“black box” objects with no important internal
structure.
There is also a rawnum symbol, which is a
combination of raw (returning a raw Calc object) and
num (signaling an error if that object is not a
constant).
You can pass a raw Calc object to calc-eval in
place of a string, either as the formula itself or as one of the
‘$’ arguments. Thus
‘(calc-eval "$+$$" 'raw a b)’ is an
addition function that operates on raw Calc objects. Of course in
this case it would be easier to call the low-level
math-add function in Calc, if you can remember its
name.
In particular, note that a plain Lisp integer is acceptable to Calc as a raw object. (All Lisp integers are accepted on input, but integers of more than six decimal digits are converted to “big-integer” form for output. See Data Type Formats.)
When it comes time to display the object, just use ‘(calc-eval a)’ to format it as a string.
It is an error if the input expression evaluates to a list of
values. The separator symbol list is like
raw except that it returns a list of one or more raw
Calc objects.
Note that a Lisp string is not a valid Calc object, nor is a list containing a string. Thus you can still safely distinguish all the various kinds of error returns discussed above.
If the separator symbol is pred, the result of
the formula is treated as a true/false value;
calc-eval returns t or
nil, respectively. A value is considered
“true” if it is a non-zero number, or false if it is
zero or if it is not a number.
For example, ‘(calc-eval "$<$$" 'pred a b)’ tests whether one value is less than another.
As usual, it is also possible for calc-eval to
return one of the error indicators described above. Lisp will
interpret such an indicator as “true” if you
don’t check for it explicitly. If you wish to have an error
register as “false”, use something like
‘(eq (calc-eval ...) t)’.
Variables in the formula passed to calc-eval are
not normally replaced by their values. If you wish this, you can
use the evalv function (see Algebraic
Manipulation). For example, if 4 is stored in Calc variable
a (i.e., in Lisp variable var-a), then
‘(calc-eval "a+pi")’ will return the
formula "a + pi", but ‘(calc-eval
"evalv(a+pi)")’ will return
"7.14159265359".
To store in a Calc variable, just use setq to
store in the corresponding Lisp variable. (This is obtained by
prepending ‘var-’ to the Calc variable
name.) Calc routines will understand either string or raw form
values stored in variables, although raw data objects are much
more efficient. For example, to increment the Calc variable
a:
(setq var-a (calc-eval "evalv(a+1)" 'raw))
If the separator symbol is push, the formula
argument is evaluated (with possible ‘$’
expansions, as usual). The result is pushed onto the Calc stack.
The return value is nil (unless there is an error
from evaluating the formula, in which case the return value
depends on calc-eval-error in the usual way).
If the separator symbol is pop, the first
argument to calc-eval must be an integer instead of
a string. That many values are popped from the stack and thrown
away. A negative argument deletes the entry at that stack level.
The return value is the number of elements remaining in the stack
after popping; ‘(calc-eval 0 'pop)’ is a
good way to measure the size of the stack.
If the separator symbol is top, the first
argument to calc-eval must again be an integer. The
value at that stack level is formatted as a string and returned.
Thus ‘(calc-eval 1 'top)’ returns the
top-of-stack value. If the integer is out of range,
nil is returned.
The separator symbol rawtop is just like
top except that the stack entry is returned as a raw
Calc object instead of as a string.
In all of these cases the first argument can be made a list in order to force the default mode settings, as described above. Thus ‘(calc-eval '(2 calc-number-radix 16) 'top)’ returns the second-to-top stack entry, formatted as a string using the default instead of current display modes, except that the radix is hexadecimal instead of decimal.
It is, of course, polite to put the Calc stack back the way you found it when you are done, unless the user of your program is actually expecting it to affect the stack.
Note that you do not actually have to switch into the
*Calculator* buffer in order to use
calc-eval; it temporarily switches into the stack
buffer if necessary.
If the separator symbol is macro, the first
argument must be a string of characters which Calc can execute as
a sequence of keystrokes. This switches into the Calc buffer for
the duration of the macro. For example, ‘(calc-eval
"vx5\rVR+" 'macro)’ pushes the vector
‘[1,2,3,4,5]’ on the stack and then
replaces it with the sum of those numbers. Note that
‘\r’ is the Lisp notation for the
carriage-return, RET, character.
If your keyboard macro wishes to pop the stack, ‘\C-d’ is safer than ‘\177’ (the DEL character) because some installations may have switched the meanings of DEL and C-h. Calc always interprets C-d as a synonym for “pop-stack” regardless of key mapping.
If you provide a third argument to calc-eval,
evaluation of the keyboard macro will leave a record in the Trail
using that argument as a tag string. Normally the Trail is
unaffected.
The return value in this case is always
nil.
Finally, if the separator symbol is eval, then
the Lisp eval function is called on the first
argument, which must be a Lisp expression rather than a Calc
formula. Remember to quote the expression so that it is not
evaluated until inside calc-eval.
The difference from plain eval is that
calc-eval switches to the Calc buffer before
evaluating the expression. For example, ‘(calc-eval
'(setq calc-internal-prec 17) 'eval)’ will correctly
affect the buffer-local Calc precision variable.
An alternative would be ‘(calc-eval
'(calc-precision 17) 'eval)’. This is evaluating a
call to the function that is normally invoked by the p
key, giving it 17 as its “numeric prefix argument.”
Note that this function will leave a message in the echo area as
a side effect. Also, all Calc functions switch to the Calc buffer
automatically if not invoked from there, so the above call is
also equivalent to ‘(calc-precision 17)’
by itself. In all cases, Calc uses save-excursion to
switch back to your original buffer when it is done.
As usual the first argument can be a list that begins with a Lisp expression to use default instead of current mode settings.
The result of calc-eval in this usage is just the
result returned by the evaluated Lisp expression.
Here is a sample Emacs command that uses
calc-eval. Suppose you have a document with lots of
references to temperatures on the Fahrenheit scale, say
“98.6 F”, and you wish to convert these references to
Centigrade. The following command does this conversion. Place the
Emacs cursor right after the letter “F” and invoke
the command to change “98.6 F” to “37 C”.
Or, if the temperature is already in Centigrade form, the command
changes it back to Fahrenheit.
(defun convert-temp ()
(interactive)
(save-excursion
(re-search-backward "[^-.0-9]\\([-.0-9]+\\) *\\([FC]\\)")
(let* ((top1 (match-beginning 1))
(bot1 (match-end 1))
(number (buffer-substring top1 bot1))
(top2 (match-beginning 2))
(bot2 (match-end 2))
(type (buffer-substring top2 bot2)))
(if (equal type "F")
(setq type "C"
number (calc-eval "($ - 32)*5/9" nil number))
(setq type "F"
number (calc-eval "$*9/5 + 32" nil number)))
(goto-char top2)
(delete-region top2 bot2)
(insert-before-markers type)
(goto-char top1)
(delete-region top1 bot1)
(if (string-match "\\.$" number) ; change "37." to "37"
(setq number (substring number 0 -1)))
(insert number))))
Note the use of insert-before-markers when
changing between “F” and “C”, so that the
character winds up before the cursor instead of after it.
Next: Internals, Previous: Example Definitions, Up: Lisp Definitions [Contents][Index]